(define-module (grump system 7z)
  #:use-module (grump files)
  #:use-module (grump lines)
  #:use-module (grump system)
  #:use-module (ice-9 regex)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-26)
  #:export (7z-executable
            7z-list
            7z-entry?
            7z-entry-datetime
            7z-entry-attr
            7z-entry-size
            7z-entry-compressed
            7z-entry-name
            7z-entry-is-dir?))

(define-once 7z-executable
  (make-parameter "7z"))

(define-record-type <7z-entry>
  (make-7z-entry datetime attr size compressed name)
  7z-entry?
  (datetime   7z-entry-datetime)
  (attr       7z-entry-attr)
  (size       7z-entry-size)
  (compressed 7z-entry-compressed)
  (name       7z-entry-name))

(define (7z-entry-is-dir? entry)
  (string-prefix? "D" (7z-entry-attr entry)))

(define 7z-entry-rx
  ;; TODO: rewrite using peg or irregex
  (make-regexp (string-append "^([0-9]{4}-[0-9]{2}-[0-9]{2}" ;; date
                              " [0-9]{2}:[0-9]{2}:[0-9]{2})" ;; time
                              " ([^ ]{5})"                   ;; attr
                              " +([0-9]+)"                   ;; size
                              " ( +([0-9]+) )?"              ;; compressed
                              " +(.*)$")))                   ;; name

(define (parse-7z-entry line)
  (and=> (regexp-exec 7z-entry-rx line)
         (lambda (m)
           (make-7z-entry (match:substring m 1)                  ;; datetime
                          (match:substring m 2)                  ;; attr
                          (string->number (match:substring m 3)) ;; size
                          (string->number (match:substring m 5)) ;; compressed
                          (match:substring m 6)))))              ;; name

(define (7z-list archive)
  (let* ((sep (cut string-prefix? "----------" <>))
         (cmd (list (7z-executable) "l" (expand-file-name archive #f)))
         (out (call-with-input-command cmd (cut take-lines-between sep sep <>))))
    (map parse-7z-entry out)))
